/*-
 * Copyright (c) 2013 Shang-Rong Cai. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $ NUMTK: validate_numtk.c    Rev. 0.0.2    2013-11-06T12:24+08:00 $
 */

#include <stdlib.h>
#include <stdio.h>
#include <time.h>

#include <bscerr.h>

#include <numtk.h>

void random_position(size_t sz, size_t round, sidx_t *idx);

void ptrz_all_zero(size_t sz, sidx_t *cnt);

void ptrz_count_postition(sidx_t brk, size_t sz, sidx_t *idx, sidx_t *cnt);


void difk_random_values(difk_t *difk);

int test_difk(size_t rz, size_t cz, size_t kz);


int cifk_random_values(cifk_t *cifk);

int test_cifk(size_t rz, size_t cz, size_t kz);


void didk_random_values(didk_t *didk);

int test_didk(size_t rz, size_t cz, size_t kz);


int cidk_random_values(cidk_t *cidk);

int test_cidk(size_t rz, size_t cz, size_t kz);


#define ROW_SIZE 18
#define COL_SIZE 16

#define NOZ_SIZE 48


int main(int argc, char *argv[])
{
    int ierr = ERRNO_UNDEFINED;

    size_t r_sz;
    size_t c_sz;
    size_t k_sz;

    r_sz = ROW_SIZE;
    c_sz = COL_SIZE;
    k_sz = NOZ_SIZE;

    srandom((unsigned long) time(NULL));


    ierr = test_difk(r_sz, c_sz, k_sz);
    ierr = test_cifk(r_sz, c_sz, k_sz);

    ierr = test_didk(r_sz, c_sz, k_sz);
    ierr = test_cidk(r_sz, c_sz, k_sz);

    return ierr;
}


void random_position(size_t sz, size_t round, sidx_t *idx)
{
    size_t zi;
    size_t idx_a;
    size_t idx_b;
    sidx_t buf;

    for (zi = 0; zi < sz; zi++)
    {
        idx[zi] = (sidx_t) zi;
    }

    for (zi = 0; zi < round; zi++)
    {
        idx_a = (size_t) random() % sz;
        idx_b = (size_t) random() % sz;

        buf = idx[idx_a];
        idx[idx_a] = idx[idx_b];
        idx[idx_b] = buf;
    }
}


void ptrz_all_zero(size_t sz, sidx_t *cnt)
{
    size_t zi;

    for (zi = 0; zi < sz; zi++)
    {
        cnt[zi] = 0;
    }
}


void ptrz_count_postition(sidx_t brk, size_t sz, sidx_t *idx, sidx_t *cnt)
{
    size_t zi;
    sidx_t cmpidx;

    for (zi = 0; zi < sz; zi++)
    {
        cmpidx = idx[zi] / brk;
        cnt[cmpidx]++;
    }
}


void difk_random_values(difk_t *difk)
{
    size_t zi;
    size_t mbkz;  /* Maximum block size */
    sidx_t *rand_idx_lst;  /* List of random index with range 0 to (mkbz - 1) */

    mbkz = difk->r_sz * difk->c_sz;

    rand_idx_lst = (sidx_t*) calloc(mbkz, sizeof(sidx_t));

    random_position(mbkz, mbkz, rand_idx_lst);

    difk->e_sz = difk->k_sz;

    for (zi = 0; zi < difk->e_sz; zi++)
    {
        difk->item[zi].r_idx = rand_idx_lst[zi] / (size_t) difk->c_sz;
        difk->item[zi].c_idx = rand_idx_lst[zi] % (size_t) difk->c_sz;
        difk->item[zi].value = (float) zi;
    }

    free(rand_idx_lst);
}


int test_difk(size_t rz, size_t cz, size_t kz)
{
    int ierr = ERRNO_UNDEFINED;

    difk_t difk;

    printf("TEST DIFK:\n");

    ierr = difk_init(rz, cz, kz, &difk);

    difk_random_values(&difk);

    difk_stdout(&difk);

    ierr = difk_dest(&difk);

    return ierr;
}


int cifk_random_values(cifk_t *cifk)
{
    int ierr = ERRNO_UNDEFINED;

    size_t zi;
    size_t mbkz;  /* Maximum block size */
    size_t ociz;  /* Order of compressed index size */
    size_t brkz;  /* Break size */
    sidx_t *rand_idx_lst;  /* List of random index with range 0 to (mkbz - 1) */
    sidx_t *ptrz;
    sidx_t shft_ptr;
    sidx_t cmpidx, sndidx;

    switch (cifk->flag)
    {
        case ROW_WISE:
            ociz = cifk->r_sz;
            brkz = cifk->c_sz;
            break;
        case COL_WISE:
            ociz = cifk->c_sz;
            brkz = cifk->r_sz;
            break;
        case RDR_NONE:
            ierr = ERRNO_SUPPORT_NONE;
            goto EXIT_CIFK_RANDOM_VALUES;
            break;
        default:
            ierr = ERRNO_UNKNOWN_TYPE;
            goto EXIT_CIFK_RANDOM_VALUES;
            break;
    }

    mbkz = cifk->r_sz * cifk->c_sz;

    rand_idx_lst = (sidx_t*) calloc(mbkz, sizeof(sidx_t));
    ptrz = (sidx_t*) calloc(ociz, sizeof(sidx_t));

    random_position(mbkz, mbkz, rand_idx_lst);
    ptrz_all_zero(ociz, ptrz);

    cifk->e_sz = cifk->k_sz;

    ptrz_count_postition(brkz, cifk->e_sz, rand_idx_lst, ptrz);

    /* Setup stripe pointer */

    shft_ptr = 0;
    for (zi = 0; zi < ociz; zi++)
    {
        cifk->sptr[zi].head = shft_ptr;
        cifk->sptr[zi].tail = shft_ptr;
        shft_ptr += ptrz[zi];
    }

    for (zi = 0; zi < cifk->e_sz; zi++)
    {
        cmpidx = rand_idx_lst[zi] / brkz;
        sndidx = rand_idx_lst[zi] % brkz;

        cifk->item[cifk->sptr[cmpidx].tail].index = sndidx;
        cifk->item[cifk->sptr[cmpidx].tail].value = (float) zi;
        cifk->sptr[cmpidx].tail++;
    }

    free(rand_idx_lst);
    free(ptrz);

    EXIT_CIFK_RANDOM_VALUES:

    return ierr;
}


int test_cifk(size_t rz, size_t cz, size_t kz)
{
    int ierr = ERRNO_UNDEFINED;

    cifk_t cifk;

    printf("TEST CIFK:\n");
    printf("ROW-WISE:\n");
    ierr = cifk_init(rz, cz, kz, ROW_WISE, &cifk);

    cifk_random_values(&cifk);

    cifk_stdout(&cifk);

    ierr = cifk_dest(&cifk);

    printf("COLUMN-WISE:\n");
    ierr = cifk_init(rz, cz, kz, COL_WISE, &cifk);

    cifk_random_values(&cifk);

    cifk_stdout(&cifk);

    ierr = cifk_dest(&cifk);

    return ierr;
}


void didk_random_values(didk_t *didk)
{
    size_t zi;
    size_t mbkz;  /* Maximum block size */
    sidx_t *rand_idx_lst;  /* List of random index with range 0 to (mkbz - 1) */

    mbkz = didk->r_sz * didk->c_sz;

    rand_idx_lst = (sidx_t*) calloc(mbkz, sizeof(sidx_t));

    random_position(mbkz, mbkz, rand_idx_lst);

    didk->e_sz = didk->k_sz;

    for (zi = 0; zi < didk->e_sz; zi++)
    {
        didk->item[zi].r_idx = rand_idx_lst[zi] / (size_t) didk->c_sz;
        didk->item[zi].c_idx = rand_idx_lst[zi] % (size_t) didk->c_sz;
        didk->item[zi].value = (double) zi;
    }

    free(rand_idx_lst);
}


int test_didk(size_t rz, size_t cz, size_t kz)
{
    int ierr = ERRNO_UNDEFINED;

    didk_t didk;

    printf("TEST DIDK:\n");

    ierr = didk_init(rz, cz, kz, &didk);

    didk_random_values(&didk);

    didk_stdout(&didk);

    ierr = didk_dest(&didk);

    return ierr;
}


int cidk_random_values(cidk_t *cidk)
{
    int ierr = ERRNO_UNDEFINED;

    size_t zi;
    size_t mbkz;  /* Maximum block size */
    size_t ociz;  /* Order of compressed index size */
    size_t brkz;  /* Break size */
    sidx_t *rand_idx_lst;  /* List of random index with range 0 to (mkbz - 1) */
    sidx_t *ptrz;
    sidx_t shft_ptr;
    sidx_t cmpidx, sndidx;

    switch (cidk->flag)
    {
        case ROW_WISE:
            ociz = cidk->r_sz;
            brkz = cidk->c_sz;
            break;
        case COL_WISE:
            ociz = cidk->c_sz;
            brkz = cidk->r_sz;
            break;
        case RDR_NONE:
            ierr = ERRNO_SUPPORT_NONE;
            goto EXIT_cidk_RANDOM_VALUES;
            break;
        default:
            ierr = ERRNO_UNKNOWN_TYPE;
            goto EXIT_cidk_RANDOM_VALUES;
            break;
    }

    mbkz = cidk->r_sz * cidk->c_sz;

    rand_idx_lst = (sidx_t*) calloc(mbkz, sizeof(sidx_t));
    ptrz = (sidx_t*) calloc(ociz, sizeof(sidx_t));

    random_position(mbkz, mbkz, rand_idx_lst);
    ptrz_all_zero(ociz, ptrz);

    cidk->e_sz = cidk->k_sz;

    ptrz_count_postition(brkz, cidk->e_sz, rand_idx_lst, ptrz);

    /* Setup stripe pointer */

    shft_ptr = 0;
    for (zi = 0; zi < ociz; zi++)
    {
        cidk->sptr[zi].head = shft_ptr;
        cidk->sptr[zi].tail = shft_ptr;
        shft_ptr += ptrz[zi];
    }

    for (zi = 0; zi < cidk->e_sz; zi++)
    {
        cmpidx = rand_idx_lst[zi] / brkz;
        sndidx = rand_idx_lst[zi] % brkz;

        cidk->item[cidk->sptr[cmpidx].tail].index = sndidx;
        cidk->item[cidk->sptr[cmpidx].tail].value = (double) zi;
        cidk->sptr[cmpidx].tail++;
    }

    free(rand_idx_lst);
    free(ptrz);

    EXIT_cidk_RANDOM_VALUES:

    return ierr;
}


int test_cidk(size_t rz, size_t cz, size_t kz)
{
    int ierr = ERRNO_UNDEFINED;

    cidk_t cidk;

    printf("TEST CIDK:\n");
    printf("ROW-WISE:\n");
    ierr = cidk_init(rz, cz, kz, ROW_WISE, &cidk);

    cidk_random_values(&cidk);

    cidk_stdout(&cidk);

    ierr = cidk_dest(&cidk);

    printf("COLUMN-WISE:\n");
    ierr = cidk_init(rz, cz, kz, COL_WISE, &cidk);

    cidk_random_values(&cidk);

    cidk_stdout(&cidk);

    ierr = cidk_dest(&cidk);

    return ierr;
}
